package com.lagou.edu.mvcframework.pojo;

import com.lagou.edu.mvcframework.annotations.Interceptor;
import com.lagou.edu.mvcframework.constants.RequestAttributes;
import com.lagou.edu.mvcframework.interceptor.AfterHandler;
import com.lagou.edu.mvcframework.interceptor.AroundHandler;
import com.lagou.edu.mvcframework.interceptor.BeforeHandler;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * @author liuc
 * @date 2021/4/22
 * @description handler的执行器
 **/
public class Invoker {
    //请求method封装
    private Handler handler;
    //请求前置，后置处理器
    private AroundHandler aroundHandler;

    public Invoker(Handler handler, AroundHandler aroundHandler){
        this.handler = handler;
        this.aroundHandler = aroundHandler;
    }

    public void invokeRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 参数绑定
        // 获取所有参数类型数组，这个数组的长度就是我们最后要传入的args数组的长度
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        // 根据上述数组长度创建一个新的数组（参数数组，是要传入反射调用的）
        Object[] paraValues = new Object[parameterTypes.length];
        // 以下就是为了向参数数组中塞值，而且还得保证参数的顺序和方法中形参顺序一致
        Map<String, String[]> parameterMap = request.getParameterMap();
        // 遍历request中所有参数  （填充除了request，response之外的参数）
        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
            // name=1&name=2   name [1,2]  如同 1,2
            String value = StringUtils.join(param.getValue(), ",");
            // 如果参数和方法中的参数匹配上了，填充数组
            if (!handler.getParamIndexMapping().containsKey(param.getKey())) {
                continue;
            }
            // 方法形参确实有该参数，找到它的索引位置，对应的把参数值放入paraValues
            //name在第 2 个位置
            Integer index = handler.getParamIndexMapping().get(param.getKey());
            // 把前台传递过来的参数值填充到对应的位置去
            paraValues[index] = value;
        }
        int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        paraValues[requestIndex] = request;
        int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        paraValues[responseIndex] = response;
        // 最终调用handler的method属性
        Object result = null;
        setPermissionParams(request);
        try {
            //前置处理
            if(!aroundHandler.before(request,response)) return;
            result = handler.getMethod().invoke(handler.getController(), paraValues);
            response.getWriter().write((String) result);
        } catch (Exception e) {
            e.printStackTrace();
            //异常处理
            aroundHandler.handleException(request,response,e);
        }
        //后置处理
        aroundHandler.after(request,response);
    }

    /**
     * 设置权限参数至请求域中
     * @param request
     */
    private void setPermissionParams(HttpServletRequest request){
        request.setAttribute(RequestAttributes.HANDLER_CONTROLLER_USER,handler.getControllerUser());
        request.setAttribute(RequestAttributes.HANDLER_METHOD_USER,handler.getMethodUser());
    }
}
